package de.mhu.mandalaui.impl;

import java.io.PrintWriter;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;

import de.mhu.mandalaui.Response;
import de.mhu.mandalaui.Styleable;
import de.mhu.mandalaui.Styleable.EMPHASIS;
import de.mhu.mandalaui.Styleable.SIZE;
import de.mhu.mandalaui.actions.Action;
import de.mhu.mandalaui.attributes.Attribute;
import de.mhu.mandalaui.attributes.MultiAttribute;
import de.mhu.mandalaui.bind.Element;

public class DefaultResponse extends Response {

	PrintWriter out;
	HashMap<String, Element> attributes = new HashMap<String, Element>();
	private DefaultWindow window;
	private HashSet<String> inlineTags = new HashSet<String>();
	
	public DefaultResponse(DefaultWindow window, PrintWriter out) {
		this.window = window;
		this.out = out;

		// TODO configure 1. in window or Ui, 2. from configuration
		inlineTags.add("em");
		inlineTags.add("strong");
		inlineTags.add("small");
		inlineTags.add("span");

	}
	
	@Override
	public void line(String line) {
		out.println(line); // encode?!
	}

	@Override
	public void startTag(String name, String clazz, Styleable style,
			String... attributes) {
		out.print('<');
		out.print(name);

		String clazz2 = style != null ? style.getStyleClass() : null;
		
		EMPHASIS emphasisStyle = style != null ? style.getEmphasisStyle() : null;
		String emphasisBase = style != null ? style.getStyleBase() : null;
		if (emphasisStyle != null) {
			if (emphasisBase == null)
				emphasisBase = emphasisStyle.toString().toLowerCase();
			else
				emphasisBase = emphasisBase + "-" + emphasisStyle.toString().toLowerCase();
			if (clazz == null)
				clazz = emphasisBase;
			else
				clazz = clazz + " " + emphasisBase;
		}
		
		SIZE sizeStyle = style != null ? style.getSizeStyle() : null;
		String sizeBase = style != null ? style.getStyleBase() : null;
		if (sizeStyle != null && sizeStyle != SIZE.DEFAULT) {
			if (sizeBase == null)
				sizeBase = sizeStyle.toString().toLowerCase();
			else
				sizeBase = sizeBase + "-" + sizeStyle.toString().toLowerCase();
			if (clazz == null)
				clazz = sizeBase;
			else
				clazz = clazz + " " + sizeBase;
		}
		
		if (clazz != null || clazz2 != null ) {
			out.print(" class='");
			if (clazz != null) 
				out.print(clazz);
			if (clazz != null && clazz2 != null)
				out.print(' ');
			if (clazz2 != null)
				out.print(clazz2);
			out.print('\'');
		}
		String extraStyle = style != null ? style.getExtraStyle() : null;
		if (extraStyle != null) {
			out.print(" style='");
			out.print(extraStyle);
			out.print('\'');
		}
		String dataBind = null;
		String type = null;
		if (attributes != null) {
			for (int i = 0; i < attributes.length; i+=2) {
				if (attributes.length > i+1) {
					if (attributes[i] != null && attributes[i+1] != null) {
						if (attributes[i].equals("data-bind")) {
							dataBind = dataBind == null ? attributes[i+1] : dataBind + ',' + attributes[i+1];
						} else {
							if (attributes[i].equals("type"))
								type = attributes[i+1];
							out.print(' ');
							out.print(attributes[i]);
							out.print("='");
							out.print(attributes[i+1]);
							out.print('\'');
						}
					}
				}
			}
		}
		
		if (style != null) {
			Action clickAction = style.getClickAction();
			Element value = style.getBindValue();
			Element visible = style.getBindVisible();
			MultiAttribute css = style.getBindCss();
			Attribute with = style.getBindWith();
			
			if (clickAction != null || value != null || visible != null || css != null || with != null || dataBind != null) {
				out.print(" data-bind=\"");
				boolean needComma = false;
				if (dataBind != null) {
					out.print(dataBind);
					needComma = true;
				}
				if (clickAction != null) {
					clickAction.startRender(this);
					if (needComma) out.append(',');
					out.print("click:");
					clickAction.renderBindCall(out);
					needComma = true;
				}
				if (value != null) {
					value.startRender(this);
					if (needComma) out.append(',');
					if (name.equals("input")) {
						if ("checkbox".equals(type))
							out.print("valueUpdate: 'input', checked:"); // TODO for old browsers use 'afterkeydown'
						else
							out.print("valueUpdate: 'input', value:"); // TODO for old browsers use 'afterkeydown'
					} else
						out.print("text:");
					value.renderBindCall(out);
					needComma = true;
				}
				if (visible != null) {
					visible.startRender(this);
					if (needComma) out.append(',');
					out.print("visible:");
					visible.renderBindCall(out);
					needComma = true;
				}
				if (css != null) {
					css.startRender(this);
					if (needComma) out.append(',');
					out.print("css: {");
					css.renderBindCall(out);
					out.print("}");
					needComma = true;
				}
				if (with != null) {
					with.startRender(this);
					if (needComma) out.append(',');
					out.print("with: ");
					with.renderBindCall(out);
					needComma = true;
				}
				out.print('"');
			}
			
		}
		
		if (isInlineTag(name))
			out.print('>');
		else
			out.println('>');
	}

	public void registerAttribute(Element value) {
		if (value == null) return;
		if (value.getOwner() == null) value.setOwner(window);
		value.doPrepare();
		if ( attributes.containsKey(value.getId()) ) return;
		attributes.put(value.getId(), value);
		if (window.getUi().isDebugDeadly() && window.getUi().isDebugFunctions()) {out.print("/* register '");out.print(value.getId());out.print("'*/'");}
		value.doRegister(this);
	}
	
	public void renderAttributes() {
		out.println("<script type='text/javascript'>");
		out.print("var "+window.getId()+" = {");
		
		boolean first =true;
		for (Map.Entry<String, Element> item : attributes.entrySet()) {
			if (item.getValue().needFunctionImplementation()) {
				if (!first) out.print(',');
				out.print('\n');
				out.print(item.getKey());
				out.print(':');
				item.getValue().renderFunctionImplementation(out);
				first = false;
			} else
			if (window.getUi().isDebugFunctions()) {
				out.print("// '");
				out.print(item.getValue().getId());
				out.println("' not implemented");
				
			}
		}
		
		out.println();
		out.println("};\nko.applyBindings("+window.getId()+");");
		window.getInitialAction().renderFunctionCall(out);
		out.println("</script>");

	}

	@Override
	public void endTag(String name) {
		out.print("</");
		out.print(name);
		if (isInlineTag(name))
			out.print('>');
		else
			out.println('>');
	}

	public boolean isInlineTag(String name) {
		return inlineTags.contains(name);
	}

	@Override
	public void append(String text) {
		out.print(text); // encode?!
	}

}
